package org.noote.test_computervision;

import java.text.DecimalFormat;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemSelectedListener;

import org.noote.libs.camera.surface.CameraSurface;
import org.noote.libs.camera.helper.Helper_Camera;
import org.noote.libs.computervision.Intensity;
import org.noote.libs.computervision.Emboss;
import org.noote.libs.computervision.buffer.Buffer8888XRGB;
import org.noote.libs.computervision.buffer.Buffer8I;
import org.noote.libs.computervision.detection.blob.BasicColorDetection;
import org.noote.libs.computervision.detection.blob.SimpleColorDetection;
import org.noote.libs.computervision.detection.blob.BlobSpace;
import org.noote.libs.computervision.detection.blob.TwoPassDetection;
import org.noote.libs.computervision.detection.edge.KirschDetection;
import org.noote.libs.computervision.detection.edge.SobelDetection;
import org.noote.libs.computervision.detection.edge.PrewittDetection;
import org.noote.libs.computervision.detection.edge.RobertsDetection;
import org.noote.libs.computervision.detection.edge.LaplaceDetection;
import org.noote.libs.computervision.detection.movement.MovementDetection_Grid;
import org.noote.libs.computervision.detection.movement.MovementDetection_Map;
import org.noote.libs.computervision.math.Color8;
import org.noote.libs.computervision.math.Point2D;
import org.noote.libs.graphicbuffer.GraphicBuffer;
import org.noote.libs.graphicbuffer.GraphicView;
import org.noote.libs.graphicbuffer.FPSCounter;

@SuppressLint("HandlerLeak")
public class TestComputerVision_Activity extends Activity {
	protected static final int MSG_UPDATE_FPSCOUNTER = 0;
	
	private CameraSurface _camera = new CameraSurface();
	
	private OutputView _ouputView;
	
	private FPSCounter _fpsOuput = new FPSCounter();
	
	private int _cvMode = 0;
	private int _cvEffectCamera = 0, _cvEffectShade = 0, _cvEffectTone = 0, _cvEffectEdge = 0, _cvEffectMovement = 0, _cvEffectBlob = 0;
	
	protected  final Lock viewOuputLock = new ReentrantLock();
	
	Buffer8888XRGB _frameInXRGB = new Buffer8888XRGB();
	Buffer8888XRGB _frameOutXRGB = new Buffer8888XRGB();
	
	Intensity _frameIntensity = new Intensity();
	Emboss _frameEmboss = new Emboss();
	
	SobelDetection _frameSobel = new SobelDetection();
	PrewittDetection _framePrewitt = new PrewittDetection();
	LaplaceDetection _frameLaplace = new LaplaceDetection();
	RobertsDetection _frameRoberts = new RobertsDetection();
	KirschDetection _frameKirsch = new KirschDetection();
	
	MovementDetection_Map _frameMovementMap = new MovementDetection_Map();
	MovementDetection_Grid _frameMovementGrid = new MovementDetection_Grid(3*3, 4*3);
	
	BasicColorDetection _blobDetectionBasic = new BasicColorDetection();
	SimpleColorDetection _blobDetectionSimple = new SimpleColorDetection();
	TwoPassDetection _blobDetectionTwoPasses = new TwoPassDetection(); 
	
	GraphicBuffer _frameGraphic = new GraphicBuffer();

	public class OutputView extends GraphicView {
		
		public OutputView(Context context) {
	        super(context);
	    }
		
		@Override
		protected void onSizeChanged(int width, int height, int width_old, int height_old)
		{
			super.onSizeChanged(width, height, width_old, height_old);

			createBuffer(width, height);
		}
	}
	
	public class CameraPreviewListener implements org.noote.libs.camera.listener.CameraPreviewListener {
		@Override
		public void onStartPreview() {
		}

		@Override
		public void onGrabbedFrame(GraphicBuffer frameCamera)
		{
			if (viewOuputLock.tryLock())
			{
				try
				{
					switch(_cvMode)
					{
					case 0:
					default:
						switch(_cvEffectCamera)
						{
						case 0:
						default:
							_ouputView.push(frameCamera);
							break;
						}
						break;
					case 1:
						switch(_cvEffectShade)
						{
						case 0:
						default:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameIntensity.calculIntensityFrom(_frameInXRGB);
								_frameIntensity.exportTo(_frameOutXRGB);
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						case 1:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameEmboss.do3x3(_frameInXRGB);
								_frameEmboss.exportTo(_frameOutXRGB);
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						}
						break;
					case 2:
						switch(_cvEffectTone)
						{
						case 0:
						default:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameIntensity.calculIntensityFrom(_frameInXRGB);
								_frameIntensity.exportAsColoredTo(_frameOutXRGB, Color8.Sepia);
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						case 1:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameIntensity.calculIntensityFrom(_frameInXRGB);
								_frameIntensity.exportAsColoredTo(_frameOutXRGB, Color8.Selenium);
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						case 2:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameIntensity.calculIntensityFrom(_frameInXRGB);
								_frameIntensity.exportAsColoredTo(_frameOutXRGB, Color8.Palladium);
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						}
						break;
					case 3:
						switch(_cvEffectEdge)
						{
						case 0:
						default:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameRoberts.do2x2(_frameInXRGB, 15);
								_frameRoberts.exportEdgeTo(_frameOutXRGB);
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						case 1:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameLaplace.do3x3(_frameInXRGB, 15);
								_frameLaplace.exportEdgeTo(_frameOutXRGB);
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						case 2:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_framePrewitt.do3x3(_frameInXRGB, 15);
								_framePrewitt.exportEdgeTo(_frameOutXRGB);
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						case 3:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameSobel.do3x3(_frameInXRGB, 15);
								_frameSobel.exportEdgeTo(_frameOutXRGB);
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						case 4:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameKirsch.do3x3(_frameInXRGB, 15);
								_frameKirsch.exportEdgeTo(_frameOutXRGB);
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						}
						break;
					case 4:
						switch(_cvEffectMovement)
						{
						case 0:
						default:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameMovementMap.push(_frameInXRGB, _fpsOuput.getTimeSlice());
								_frameMovementMap.exportTo(_frameOutXRGB);
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						case 1:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameMovementGrid.push(_frameInXRGB, _fpsOuput.getTimeSlice());
								
								// get frame
								_frameInXRGB.fill(0x000000);
								_frameInXRGB.add(_frameMovementGrid);
								int iColumnSize = _frameInXRGB.getWidth()/_frameMovementGrid.getColumnCount();
								int iRowSize = _frameInXRGB.getHeight()/_frameMovementGrid.getRowCount();
								for(int y=0; y<_frameMovementGrid.getRowCount(); y++)
								{
									for(int x=0; x<_frameMovementGrid.getColumnCount(); x++)
									{
										float fA = _frameMovementGrid.getActivity(x, y); 
										if(fA>0.25f)
										{
											_frameInXRGB.drawFilledRect(new Point2D(iColumnSize*x+1, iRowSize*y+1), new Point2D(iColumnSize-1, iRowSize-1), 0x00FF1F1F);
										}
										else
										{
											_frameInXRGB.drawRect(new Point2D(iColumnSize*x, iRowSize*y), new Point2D(iColumnSize, iRowSize), 0x00FF0000);
										}
									}
								}
								
								// push frame !
								_frameInXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						}
						break;
					case 5:
						switch(_cvEffectBlob)
						{
						case 0:
						default:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameOutXRGB.createBuffer(_frameInXRGB.getWidth(), _frameInXRGB.getHeight());
								_frameOutXRGB.fill(0);
								
								BlobSpace blob = new BlobSpace();
								if(_blobDetectionBasic.detect(_frameInXRGB, BasicColorDetection.ColorType.BLUE, false, true, blob))
								{
									Buffer8I mask = blob.mask; 
									if(mask != null)
									{
										for(int y=0; y<mask.getHeight(); y++)
										{
											for(int x=0; x<mask.getWidth(); x++)
											{
												if(mask.getPixel(x, y)>0)
												{
													_frameOutXRGB.drawPixel(blob.x+x, blob.y+y, _frameInXRGB.getPixel(blob.x+x, blob.y+y));
												}
											}
										}
									}
								}
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);								
							}
							break;
						case 1:
							if(_frameInXRGB.createFrom(frameCamera))
							{
								_frameOutXRGB.createBuffer(_frameInXRGB.getWidth(), _frameInXRGB.getHeight());
								_frameOutXRGB.fill(0);
								
								BlobSpace blob = new BlobSpace();
								if(_blobDetectionSimple.detect(_frameInXRGB, 0x001F0000, 0x00FF3F3F, false, true, blob))
								{
									Buffer8I mask = blob.mask; 
									if(mask != null)
									{
										for(int y=0; y<mask.getHeight(); y++)
										{
											for(int x=0; x<mask.getWidth(); x++)
											{
												if(mask.getPixel(x, y)>0)
												{
													_frameOutXRGB.drawPixel(blob.x+x, blob.y+y, _frameInXRGB.getPixel(blob.x+x, blob.y+y));
												}
											}
										}
									}
								}
								
								// push frame !
								_frameOutXRGB.copyTo(_frameGraphic);
								_ouputView.push(_frameGraphic);
							}
							break;
						}
						break;
					}
				} finally {
					viewOuputLock.unlock();
					_fpsOuput.tick();
					mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_FPSCOUNTER, null));					
				}
			}
		}

		@Override
		public void onStopPreview() {
		}
	}
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		if(Helper_Camera.checkCameraHardware(getApplicationContext()))
		{
			_camera.setPreviewListener(new CameraPreviewListener());
		}
		else
		{
			Toast.makeText(getApplicationContext(), "Your device have no camera !", Toast.LENGTH_LONG).show();
		}

		_ouputView = new OutputView(this);
		((FrameLayout) findViewById(R.id.layoutOutput)).addView(_ouputView);
		
		Spinner spinnerEffect = (Spinner) findViewById(R.id.spinnerEffect);
		spinnerEffect.setOnItemSelectedListener(new OnItemSelectedListener()
		{
			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					int arg2, long arg3) {
				
				_cvMode = arg2;
				
				findViewById(R.id.spinnerEffectCamera).setVisibility(_cvMode==0?View.VISIBLE:View.GONE);
				findViewById(R.id.spinnerEffectShade).setVisibility(_cvMode==1?View.VISIBLE:View.GONE);
				findViewById(R.id.spinnerEffectTone).setVisibility(_cvMode==2?View.VISIBLE:View.GONE);
				findViewById(R.id.spinnerEffectEdge).setVisibility(_cvMode==3?View.VISIBLE:View.GONE);
				findViewById(R.id.spinnerEffectMovement).setVisibility(_cvMode==4?View.VISIBLE:View.GONE);
				findViewById(R.id.spinnerEffectBlob).setVisibility(_cvMode==5?View.VISIBLE:View.GONE);
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				_cvMode = 0;
			}
		});
		spinnerEffect.setSelection(_cvMode);

		Spinner spinnerEffectCamera = (Spinner) findViewById(R.id.spinnerEffectCamera);
		spinnerEffectCamera.setOnItemSelectedListener(new OnItemSelectedListener()
		{
			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					int arg2, long arg3) {
				
				_cvEffectCamera = arg2;
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				_cvEffectCamera = 0;
			}
		});
		spinnerEffectCamera.setSelection(_cvEffectCamera);

		Spinner spinnerEffectShade = (Spinner) findViewById(R.id.spinnerEffectShade);
		spinnerEffectShade.setOnItemSelectedListener(new OnItemSelectedListener()
		{
			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					int arg2, long arg3) {
				
				_cvEffectShade = arg2;
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				_cvEffectShade = 0;
			}
		});
		spinnerEffectShade.setSelection(_cvEffectShade);

		Spinner spinnerEffectTone = (Spinner) findViewById(R.id.spinnerEffectTone);
		spinnerEffectTone.setOnItemSelectedListener(new OnItemSelectedListener()
		{
			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					int arg2, long arg3) {
				
				_cvEffectTone = arg2;
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				_cvEffectTone = 0;
			}
		});
		spinnerEffectTone.setSelection(_cvEffectTone);

		Spinner spinnerEffectEdge = (Spinner) findViewById(R.id.spinnerEffectEdge);
		spinnerEffectEdge.setOnItemSelectedListener(new OnItemSelectedListener()
		{
			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					int arg2, long arg3) {
				
				_cvEffectEdge = arg2;
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				_cvEffectEdge = 0;
			}
		});
		spinnerEffectEdge.setSelection(_cvEffectEdge);

		Spinner spinnerEffectMovement = (Spinner) findViewById(R.id.spinnerEffectMovement);
		spinnerEffectMovement.setOnItemSelectedListener(new OnItemSelectedListener()
		{
			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					int arg2, long arg3) {
				
				_cvEffectMovement = arg2;
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				_cvEffectMovement = 0;
			}
		});
		spinnerEffectMovement.setSelection(_cvEffectMovement);

		Spinner spinnerEffectBlob = (Spinner) findViewById(R.id.spinnerEffectBlob);
		spinnerEffectBlob.setOnItemSelectedListener(new OnItemSelectedListener()
		{
			@Override
			public void onItemSelected(AdapterView<?> arg0, View arg1,
					int arg2, long arg3) {
				
				_cvEffectBlob = arg2;
			}

			@Override
			public void onNothingSelected(AdapterView<?> arg0) {
				_cvEffectBlob = 0;
			}
		});
		spinnerEffectBlob.setSelection(_cvEffectBlob);

	}

	@Override
    protected void onResume() {
        super.onResume();
		if(_camera.open(Helper_Camera.getCamera()))
		{
			_camera.startPreview();
		}
		else
		{
			Toast.makeText(getApplicationContext(), "Cannot open camera !", Toast.LENGTH_LONG).show();
		}
    }

	@Override
    protected void onPause() {
		_camera.close();

		super.onPause();
	}

	final Handler mHandler = new Handler() {
		DecimalFormat m_dfFPS = new DecimalFormat("#,##0.00");
		
	    @Override
		public void handleMessage(Message msg) {
	        switch (msg.what) {
	        case MSG_UPDATE_FPSCOUNTER:
	        	TextView textOuputFPS = (TextView) findViewById(R.id.textOuputFPS);
	        	textOuputFPS.setText(m_dfFPS.format(_fpsOuput.getFPS()) + " f/s");
	            break;
	        }
	    }
	};
}
